﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Net;
using System.Net.NetworkInformation;
using System.Diagnostics;


namespace INetConnectivityMonitor
{
    public class WebDownload : WebClient
    {
        private int _timeout;
        /// <summary>
        /// Time in milliseconds
        /// </summary>
        public int Timeout
        {
            get
            {
                return _timeout;
            }
            set
            {
                _timeout = value;
            }
        }

        public WebDownload()
        {
            this._timeout = 60;
        }

        public WebDownload(int timeout)
        {
            this._timeout = timeout;
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            var result = base.GetWebRequest(address);
            result.Timeout = this._timeout;
            return result;
        }
    }
    class Program
    {
        static Uri connectivity_uri = new Uri(@"http://192.168.112.1/GethomeInfo.html");
        static Uri wan_uri = new Uri(@"http://192.168.112.1/GetWANDSLInfo.html");
        static Int64 nanosec_per_tick = (1000L * 1000L * 1000L) / System.Diagnostics.Stopwatch.Frequency;
        //GetWANDSLInfo.html // for WAN info

        
        static void Main(string[] args)
        {
            /*
            DestroyOldTable();
            UpdateDatabase();
             */
            InetData inetData = new InetData();
            while (true)
            {
                Ping ping = new Ping();
                PingReply reply = null;
                
                byte[] pingBuffer = new byte[1464];// MTU = 1492
                byte[] pingBufferSmall = new byte[8];
                Random rng = new Random();
                rng.NextBytes(pingBuffer);
                rng.NextBytes(pingBufferSmall);

                PingOptions pingOptions = new PingOptions(254, true);

                System.Diagnostics.Stopwatch stopWatch;
                stopWatch = System.Diagnostics.Stopwatch.StartNew();
                
                
                try
                {
                    // Google friendly dns server
                    reply = ping.Send("8.8.4.4", 5000, pingBuffer, pingOptions);
                    stopWatch.Stop();
                    if (reply.Status == IPStatus.Success)
                    {
                        inetData.ping_time_remote = stopWatch.ElapsedTicks * nanosec_per_tick;
                    }
                    else
                    {
                        inetData.ping_time_remote = 0;
                    }
                }
                catch (Exception e)
                {
                    inetData.ping_time_remote = 0;
                }

                stopWatch = System.Diagnostics.Stopwatch.StartNew();
                
                try
                {
                    //TODO: fill out with nearest internal gateway
                    reply = ping.Send("pos-0-3-0-0-pe01.529bryant.ca.ibone.comcast.net", 1000);//, pingBufferSmall, pingOptions);
                    stopWatch.Stop();
                    if (reply.Status == IPStatus.Success)
                    {
                        inetData.ping_time_local = stopWatch.ElapsedTicks * nanosec_per_tick;
                    }
                    else
                    {
                        inetData.ping_time_local = 0;
                    }
                }
                catch (Exception e)
                {
                    inetData.ping_time_local = 0;
                }
                

                // This is code for my specific router. Probably won't work for the general case
                //inetData.Populate(connectivity_uri);

                Console.WriteLine("Time: {0}\nConnected: {1}|{2}\nSpeed: {3}|{4}|{5}ms|{6}ms",
                    inetData.time_utc.ToString("o"), inetData.broad_connected, inetData.isp_connected,
                    inetData.up_kbs, inetData.down_kbs,Convert.ToDouble(inetData.ping_time_local)/1000000.0, Convert.ToDouble(inetData.ping_time_remote)/1000000.0);
                /**
                 * More router specific code
                if (inetData.isp_connected != inetData.ConnectionDescription["Connected"])
                {
                    Console.Write('!');
                }
                else
                {
                    Console.Write('.');
                }
                 */
                FillTable(inetData);

                System.Threading.Thread.Sleep(500);
            }
        }
        static void UpdateDatabase()
        {
            string connectionString = "server=(local);Type System Version=SQL Server 2008;Database=inet_connectivity;Trusted_Connection=true";
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand addData = new SqlCommand("CREATE TABLE INET_DATA (DT DateTime, BROAD_CONNECT int, ISP_CONNECT int, UP_SPEED int, DOWN_SPEED int, PING_TIME_LOCAL bigint, PING_TIME_REMOTE bigint)", connection);
                connection.Open();
                addData.ExecuteNonQuery();
                connection.Close();
            }
        }
        static void DestroyOldTable()
        {
            string connectionString = "server=(local);Type System Version=SQL Server 2008;Database=inet_connectivity;Trusted_Connection=true";
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand addData = new SqlCommand("DROP TABLE INET_DATA", connection);
                connection.Open();
                addData.ExecuteNonQuery();
                connection.Close();
            }
        }
        static void FillTable(InetData inetData)
        {
            String connectionString = "server=(local);Type System Version=SQL Server 2008;Database=inet_connectivity;Trusted_Connection=true";
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand addData = new SqlCommand("INSERT INTO INET_DATA WITH (TABLOCK) (DT, BROAD_CONNECT, ISP_CONNECT, UP_SPEED, DOWN_SPEED, PING_TIME_LOCAL, PING_TIME_REMOTE)" +
                            " VALUES (@DT, @BROAD_CONNECT, @ISP_CONNECT, @UP_SPEED, @DOWN_SPEED, @PING_TIME_LOCAL, @PING_TIME_REMOTE)", connection);
                addData.Parameters.Add("@DT", SqlDbType.DateTime).Value = inetData.time_utc.ToString("o");
                addData.Parameters.Add("@BROAD_CONNECT", SqlDbType.Int).Value = inetData.broad_connected;
                addData.Parameters.Add("@ISP_CONNECT", SqlDbType.Int).Value = inetData.isp_connected;
                addData.Parameters.Add("@UP_SPEED", SqlDbType.Int).Value = inetData.up_kbs;
                addData.Parameters.Add("@DOWN_SPEED", SqlDbType.Int).Value = inetData.down_kbs;
                addData.Parameters.Add("@PING_TIME_LOCAL", SqlDbType.BigInt).Value = inetData.ping_time_local;
                addData.Parameters.Add("@PING_TIME_REMOTE", SqlDbType.BigInt).Value = inetData.ping_time_remote;
                connection.Open();
                addData.ExecuteNonQuery();
                connection.Close();
            }
        }
    }
    class InetData
    {
        public Int32 up_kbs = 0;
        public Int32 down_kbs = 0;
        public Dictionary<string, int> ConnectionDescription;
        public Int32 broad_connected = 0;
        public Int32 isp_connected = 0;
        public Int64 ping_time_remote = 0;
        public Int64 ping_time_local = 0;

        public DateTime time_utc = DateTime.UtcNow;
        public InetData()
        {
            ConnectionDescription = new Dictionary<string, int>();
            ConnectionDescription.Add("Corrupted Data", -2);
            ConnectionDescription.Add("No Response", -1);
            ConnectionDescription.Add("Not Connected", 0);
            ConnectionDescription.Add("Connected", 1);
            ConnectionDescription.Add("Connecting", 2);
            ConnectionDescription.Add("Walled Garden", 3);
        }
        public void Populate(Uri uri)
        {
            String[] results;
            up_kbs = 0;
            down_kbs = 0;
            try
            {
                using (WebDownload client = new WebDownload())
                {
                    results = client.DownloadString(uri).Split('|');
                }
                broad_connected = ConnectionDescription["Corrupted Data"];
                isp_connected = ConnectionDescription["Corrupted Data"];
                if (results.Count() < 2)
                {
                    Console.WriteLine("\nCorrupted data recieved: {0}",string.Join("|",results));
                }
                else
                {
                    if (ConnectionDescription.Keys.Contains(results[0]))
                    {
                        broad_connected = ConnectionDescription[results[0]];
                    }
                    

                    if (ConnectionDescription.Keys.Contains(results[1]))
                    {
                        isp_connected = ConnectionDescription[results[1]];
                    }

                    if (results.Count() < 3)
                    {
                        Console.WriteLine("Error parsing up kbs{0}", string.Join("|", results));
                        up_kbs = -1;
                    }
                    else if (!Int32.TryParse(results[2].Split(' ')[0], out up_kbs))
                    {
                        Console.WriteLine("Error parsing up kbs{0}", results[2]);
                        up_kbs = -1;
                    }

                    if (results.Count() < 4)
                    {
                        Console.WriteLine("Error parsing down kbs{0}", string.Join("|", results));
                        down_kbs = -1;
                    }
                    else if (!Int32.TryParse(results[3].Split(' ')[0], out down_kbs))
                    {
                        Console.WriteLine("Error parsing down kbs{0}", results[3]);
                        down_kbs = -1;
                    }
                }
            }
            catch (WebException e)
            {
                broad_connected = ConnectionDescription["No Response"];
                isp_connected = ConnectionDescription["No Response"];
            }
            time_utc = DateTime.UtcNow;
        }
    }
}
